Skip to content

Conversation

@Sirraide
Copy link
Member

@Sirraide Sirraide commented Nov 26, 2025

This implements Sema and template instantiation for enumerating expansion statements, as well as expansion of expansion statements in general.

@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: None (Sirraide)

Changes

Patch is 32.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169682.diff

9 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/include/clang/Sema/Sema.h (+22)
  • (modified) clang/lib/Frontend/FrontendActions.cpp (+2)
  • (modified) clang/lib/Sema/SemaExpand.cpp (+151)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+26-3)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+37-1)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+6-2)
  • (modified) clang/lib/Sema/TreeTransform.h (+105-5)
  • (modified) clang/test/Parser/cxx2c-expansion-statements.cpp (+39-39)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ca862316a2f27..d4783c1c9677d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5827,6 +5827,8 @@ def note_template_nsdmi_here : Note<
   "in instantiation of default member initializer %q0 requested here">;
 def note_template_type_alias_instantiation_here : Note<
   "in instantiation of template type alias %0 requested here">;
+def note_expansion_stmt_instantiation_here : Note<
+  "in instantiation of expansion statement requested here">;
 def note_template_exception_spec_instantiation_here : Note<
   "in instantiation of exception specification for %0 requested here">;
 def note_template_requirement_instantiation_here : Note<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 786e53a2e179a..4d25143cffaf4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13176,6 +13176,9 @@ class Sema final : public SemaBase {
 
       /// We are performing partial ordering for template template parameters.
       PartialOrderingTTP,
+
+      /// We are instantiating an expansion statement.
+      ExpansionStmtInstantiation,
     } Kind;
 
     /// Whether we're substituting into constraints.
@@ -13371,6 +13374,12 @@ class Sema final : public SemaBase {
                           concepts::Requirement *Req,
                           SourceRange InstantiationRange = SourceRange());
 
+    /// \brief Note that we are substituting the body of an expansion statement.
+    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+                          CXXExpansionStmtPattern *ExpansionStmt,
+                          ArrayRef<TemplateArgument> TArgs,
+                          SourceRange InstantiationRange);
+
     /// \brief Note that we are checking the satisfaction of the constraint
     /// expression inside of a nested requirement.
     InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
@@ -15643,6 +15652,19 @@ class Sema final : public SemaBase {
       ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
 
   StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
+
+  StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
+                                                     Stmt *ExpansionVar,
+                                                     SourceLocation LParenLoc,
+                                                     SourceLocation ColonLoc,
+                                                     SourceLocation RParenLoc);
+
+  ExprResult
+  BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
+                                      Expr *Idx);
+
+  std::optional<uint64_t>
+  ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
   ///@}
 };
 
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e0c1d304e8290..75d5a76c04a32 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -476,6 +476,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
       return "TypeAliasTemplateInstantiation";
     case CodeSynthesisContext::PartialOrderingTTP:
       return "PartialOrderingTTP";
+    case CodeSynthesisContext::ExpansionStmtInstantiation:
+      return "ExpansionStmtInstantiation";
     }
     return "";
   }
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index c74ed63d3295a..acb28d6bbdcfb 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -24,6 +24,23 @@
 using namespace clang;
 using namespace sema;
 
+// Build a 'DeclRefExpr' designating the template parameter '__N'.
+static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
+  return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(),
+                            S.Context.getPointerDiffType(), VK_PRValue,
+                            ESD->getBeginLoc());
+}
+
+static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar,
+                                 ExprResult Initializer) {
+  if (Initializer.isInvalid()) {
+    S.ActOnInitializerError(ExpansionVar);
+    return true;
+  }
+
+  S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false);
+  return ExpansionVar->isInvalidDecl();
+}
 
 CXXExpansionStmtDecl *
 Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
@@ -69,13 +86,147 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
     Expr *ExpansionInitializer, SourceLocation LParenLoc,
     SourceLocation ColonLoc, SourceLocation RParenLoc,
     ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+  if (!ExpansionInitializer || !ExpansionVarStmt)
+    return StmtError();
+
+  assert(CurContext->isExpansionStmt());
+  auto *DS = cast<DeclStmt>(ExpansionVarStmt);
+  if (!DS->isSingleDecl()) {
+    Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range);
+    return StmtError();
+  }
+
+  VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl());
+  if (!ExpansionVar || ExpansionVar->isInvalidDecl() ||
+      ExpansionInitializer->containsErrors())
+    return StmtError();
+
+  // This is an enumerating expansion statement.
+  if (auto *ILE = dyn_cast<CXXExpansionInitListExpr>(ExpansionInitializer)) {
+    ExprResult Initializer =
+        BuildCXXExpansionInitListSelectExpr(ILE, BuildIndexDRE(*this, ESD));
+    if (FinaliseExpansionVar(*this, ExpansionVar, Initializer))
+      return StmtError();
+
+    // Note that lifetime extension only applies to destructuring expansion
+    // statements, so we just ignore 'LifetimeExtendedTemps' entirely for other
+    // types of expansion statements (this is CWG 3043).
+    return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
+                                                   ColonLoc, RParenLoc);
+  }
+
   Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
   return StmtError();
 }
 
+StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
+    Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
+    SourceLocation ColonLoc, SourceLocation RParenLoc) {
+  return new (Context) CXXEnumeratingExpansionStmtPattern(
+      cast<CXXExpansionStmtDecl>(ESD), Init, cast<DeclStmt>(ExpansionVar),
+      LParenLoc, ColonLoc, RParenLoc);
+}
+
 StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
   if (!Exp || !Body)
     return StmtError();
 
+  auto *Expansion = cast<CXXExpansionStmtPattern>(Exp);
+  assert(!Expansion->getDecl()->getInstantiations() &&
+         "should not rebuild expansion statement after instantiation");
+
+  Expansion->setBody(Body);
+  if (Expansion->hasDependentSize())
+    return Expansion;
+
+  // This can fail if this is an iterating expansion statement.
+  std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
+  if (!NumInstantiations)
+    return StmtError();
+
+  // Collect shared statements.
+  SmallVector<Stmt *, 1> Shared;
+  if (Expansion->getInit())
+    Shared.push_back(Expansion->getInit());
+
+  assert(isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && "TODO");
+
+  // Return an empty statement if the range is empty.
+  if (*NumInstantiations == 0) {
+    Expansion->getDecl()->setInstantiations(
+        CXXExpansionStmtInstantiation::Create(
+            Context, Expansion->getBeginLoc(), Expansion->getEndLoc(),
+            /*Instantiations=*/{}, Shared,
+            isa<CXXDestructuringExpansionStmtPattern>(Expansion)));
+    return Expansion;
+  }
+
+  // Create a compound statement binding the expansion variable and body.
+  Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body};
+  Stmt *CombinedBody =
+      CompoundStmt::Create(Context, VarAndBody, FPOptionsOverride(),
+                           Body->getBeginLoc(), Body->getEndLoc());
+
+  // Expand the body for each instantiation.
+  SmallVector<Stmt *, 4> Instantiations;
+  CXXExpansionStmtDecl *ESD = Expansion->getDecl();
+  for (uint64_t I = 0; I < *NumInstantiations; ++I) {
+    // Now that we're expanding this, exit the context of the expansion stmt
+    // so that we no longer treat this as dependent.
+    ContextRAII CtxGuard(*this, CurContext->getParent(),
+                         /*NewThis=*/false);
+
+    TemplateArgument Arg{Context, llvm::APSInt::get(I),
+                         Context.getPointerDiffType()};
+    MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
+    MTArgList.addOuterRetainedLevels(
+        Expansion->getDecl()->getIndexTemplateParm()->getDepth());
+
+    LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true);
+    NonSFINAEContext _(*this);
+    InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
+                               Body->getSourceRange());
+
+    StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList);
+    if (Instantiation.isInvalid())
+      return StmtError();
+    Instantiations.push_back(Instantiation.get());
+  }
+
+  auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
+      Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), Instantiations,
+      Shared, isa<CXXDestructuringExpansionStmtPattern>(Expansion));
+
+  Expansion->getDecl()->setInstantiations(InstantiationsStmt);
+  return Expansion;
+}
+
+ExprResult
+Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
+                                          Expr *Idx) {
+  if (Range->containsPackExpansion() || Idx->isValueDependent())
+    return new (Context) CXXExpansionInitListSelectExpr(Context, Range, Idx);
+
+  // The index is a DRE to a template parameter; we should never
+  // fail to evaluate it.
+  Expr::EvalResult ER;
+  if (!Idx->EvaluateAsInt(ER, Context))
+    llvm_unreachable("Failed to evaluate expansion index");
+
+  uint64_t I = ER.Val.getInt().getZExtValue();
+  return Range->getExprs()[I];
+}
+
+std::optional<uint64_t>
+Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
+  assert(!Expansion->hasDependentSize());
+
+  if (isa<CXXEnumeratingExpansionStmtPattern>(Expansion))
+    return cast<CXXExpansionInitListSelectExpr>(
+               Expansion->getExpansionVariable()->getInit())
+        ->getRangeExpr()
+        ->getExprs()
+        .size();
+
   llvm_unreachable("TODO");
 }
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 35205f40cbcef..5957af710d38e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -573,6 +573,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   case PriorTemplateArgumentSubstitution:
   case ConstraintsCheck:
   case NestedRequirementConstraintsCheck:
+  case ExpansionStmtInstantiation:
     return true;
 
   case RequirementInstantiation:
@@ -759,6 +760,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
           PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
           /*Template=*/nullptr, /*TemplateArgs=*/{}) {}
 
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+    Sema &SemaRef, SourceLocation PointOfInstantiation,
+    CXXExpansionStmtPattern *ExpansionStmt, ArrayRef<TemplateArgument> TArgs,
+    SourceRange InstantiationRange)
+    : InstantiatingTemplate(
+          SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation,
+          PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
+          /*Template=*/nullptr, /*TemplateArgs=*/TArgs) {}
+
 Sema::InstantiatingTemplate::InstantiatingTemplate(
     Sema &SemaRef, SourceLocation PointOfInstantiation,
     concepts::NestedRequirement *Req, ConstraintsCheck,
@@ -1260,6 +1270,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
                                << /*isTemplateTemplateParam=*/true
                                << Active->InstantiationRange);
       break;
+    case CodeSynthesisContext::ExpansionStmtInstantiation:
+      Diags.Report(Active->PointOfInstantiation,
+                   diag::note_expansion_stmt_instantiation_here);
     }
   }
 }
@@ -1894,6 +1907,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
       maybeInstantiateFunctionParameterToScope(PVD))
     return nullptr;
 
+  if (isa<CXXExpansionStmtDecl>(D)) {
+    assert(SemaRef.CurrentInstantiationScope);
+    return cast<Decl *>(
+        *SemaRef.CurrentInstantiationScope->findInstantiationOf(D));
+  }
+
   return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
 }
 
@@ -2288,9 +2307,13 @@ ExprResult
 TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
                                                        ValueDecl *PD) {
   typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
-  llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found
-    = getSema().CurrentInstantiationScope->findInstantiationOf(PD);
-  assert(Found && "no instantiation for parameter pack");
+  llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found =
+      getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD);
+
+  // This can happen when instantiating an expansion statement that contains
+  // a pack (e.g. `template for (auto x : {{ts...}})`).
+  if (!Found)
+    return E;
 
   Decl *TransformedDecl;
   if (DeclArgumentPack *Pack = dyn_cast<DeclArgumentPack *>(*Found)) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f8136c3c24a52..f88ddcb40adf7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2089,7 +2089,37 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
 
 Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
     CXXExpansionStmtDecl *OldESD) {
-  llvm_unreachable("TODO");
+  Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm());
+  CXXExpansionStmtDecl *NewESD = SemaRef.BuildCXXExpansionStmtDecl(
+      Owner, OldESD->getBeginLoc(), cast<NonTypeTemplateParmDecl>(Index));
+  SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD);
+
+  // If this was already expanded, only instantiate the expansion and
+  // don't touch the unexpanded expansion statement.
+  if (CXXExpansionStmtInstantiation *OldInst = OldESD->getInstantiations()) {
+    StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs);
+    if (NewInst.isInvalid())
+      return nullptr;
+
+    NewESD->setInstantiations(NewInst.getAs<CXXExpansionStmtInstantiation>());
+    NewESD->setExpansionPattern(OldESD->getExpansionPattern());
+    return NewESD;
+  }
+
+  // Enter the scope of this expansion statement; don't do this if we've
+  // already expanded it, as in that case we no longer want to treat its
+  // content as dependent.
+  Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false);
+
+  StmtResult Expansion =
+      SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs);
+  if (Expansion.isInvalid())
+    return nullptr;
+
+  // The code that handles CXXExpansionStmtPattern takes care of calling
+  // setInstantiation() on the ESD if there was an expansion.
+  NewESD->setExpansionPattern(cast<CXXExpansionStmtPattern>(Expansion.get()));
+  return NewESD;
 }
 
 Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
@@ -7093,6 +7123,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
     // anonymous unions in class templates).
   }
 
+  if (CurrentInstantiationScope) {
+    if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D))
+      if (auto *FD = dyn_cast<NamedDecl>(cast<Decl *>(*Found)))
+        return FD;
+  }
+
   if (!ParentDependsOnArgs)
     return D;
 
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..b61148bf95738 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -912,10 +912,14 @@ bool Sema::CheckParameterPacksForExpansion(
     unsigned NewPackSize, PendingPackExpansionSize = 0;
     if (IsVarDeclPack) {
       // Figure out whether we're instantiating to an argument pack or not.
+      //
+      // The instantiation may not exist; this can happen when instantiating an
+      // expansion statement that contains a pack (e.g.
+      // `template for (auto x : {{ts...}})`).
       llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
-          CurrentInstantiationScope->findInstantiationOf(
+          CurrentInstantiationScope->getInstantiationOfIfExists(
               cast<NamedDecl *>(ParmPack.first));
-      if (isa<DeclArgumentPack *>(*Instantiation)) {
+      if (Instantiation && isa<DeclArgumentPack *>(*Instantiation)) {
         // We could expand this function parameter pack.
         NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
       } else {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f181d0abb5dfd..825660fe8174e 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -67,6 +67,15 @@ struct UnexpandedInfo {
   bool ExpandUnderForgetSubstitions = false;
 };
 
+/// This contains the common parts that are instantiated for all expansion
+/// statement patterns.
+struct TransformCXXExpansionStmtPatternResult {
+  CXXExpansionStmtDecl *NewESD{};
+  Stmt *NewInit{};
+  DeclStmt *NewExpansionVarDecl{};
+  bool isValid() const { return NewESD != nullptr; }
+};
+
 /// A semantic tree transformation that allows one to transform one
 /// abstract syntax tree into another.
 ///
@@ -857,6 +866,9 @@ class TreeTransform {
 
   StmtResult TransformOMPInformationalDirective(OMPExecutableDirective *S);
 
+  TransformCXXExpansionStmtPatternResult
+  TransformCXXExpansionStmtPatternCommonParts(CXXExpansionStmtPattern *S);
+
 // FIXME: We use LLVM_ATTRIBUTE_NOINLINE because inlining causes a ridiculous
 // amount of stack usage with clang.
 #define STMT(Node, Parent)                        \
@@ -9292,10 +9304,49 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
   return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
 }
 
+template <typename Derived>
+TransformCXXExpansionStmtPatternResult
+TreeTransform<Derived>::TransformCXXExpansionStmtPatternCommonParts(
+    CXXExpansionStmtPattern *S) {
+  Decl *ESD =
+      getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl());
+  if (!ESD || ESD->isInvalidDecl())
+    return {};
+
+  Stmt *Init = S->getInit();
+  if (Init) {
+    StmtResult SR = getDerived().TransformStmt(Init);
+    if (SR.isInvalid())
+      return {};
+    Init = SR.get();
+  }
+
+  StmtResult ExpansionVar =
+      getDerived().TransformStmt(S->getExpansionVarStmt());
+  if (ExpansionVar.isInvalid())
+    return {};
+
+  return {cast<CXXExpansionStmtDecl>(ESD), Init,
+          ExpansionVar.getAs<DeclStmt>()};
+}
+
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformCXXEnumeratingExpansionStmtPattern(
     CXXEnumeratingExpansionStmtPattern *S) {
-  llvm_unreachable("TOOD");
+  TransformCXXExpansionStmtPatternResult Common =
+      TransformCXXExpansionStmtPatternCommonParts(S);
+  if (!Common.isValid())
+    return StmtError();
+
+  auto *Expansion = new (SemaRef.Context) CXXEnumeratingExpansionStmtPattern(
+      Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl,
+      S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc());
+
+  StmtResult Body = getDerived().TransformStmt(S->getBody());
+  if (Body.isInvalid())
+    return StmtError();
+
+  return SemaRef.FinishCXXExpansionStmt(Expansion, Body.get());
 }
 
 template <typename Derived>
@@ -9324,19 +9375,68 @@ TreeTransform<Derived>::TransformCXXDestructuringExpansionStmtPattern(
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListExpr(
     CXXExpansionInitListExpr *E) {
-  llvm_unreachable("TOOD");
+  bool ArgChanged = false;
+  SmallVector<Expr *> SubExprs;
+  if (getDerived().TransformExprs(E->getExprs().data(), E->getExprs().size(),
+                                  false, SubExprs, &ArgChanged))
+    return ExprError();
+
+  if (!getDerived().AlwaysRebuild() && !ArgChanged)
+    return E;
+
+  return CXXExpansionInitListExpr::Create(SemaRe...
[truncated]

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

🐧 Linux x64 Test Results

  • 111407 tests passed
  • 4449 tests skipped

@Sirraide Sirraide marked this pull request as ready for review November 26, 2025 17:46
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Nov 26, 2025
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-2-parsing branch from cd40ea9 to c7cd02e Compare November 26, 2025 18:05
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from ad76ea8 to c4c6899 Compare November 26, 2025 18:05
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No comments here, everything looks reasonable, but I've burned out on review of this, so quitting this patch set. I'll come back when we get the other 2 settled/ready (another time...).

@Sirraide
Copy link
Member Author

Sirraide commented Dec 1, 2025

No comments here, everything looks reasonable, but I've burned out on review of this, so quitting this patch set. I'll come back when we get the other 2 settled/ready (another time...).

Fair enough haha, and thanks for the reviews so far!

@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from c4c6899 to b85d1e4 Compare December 1, 2025 17:26
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-2-parsing branch from 2d43de2 to f92568a Compare December 1, 2025 20:30
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from b85d1e4 to 56ad658 Compare December 1, 2025 20:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++26 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants